﻿using Newtonsoft.Json;
using System.Globalization;

namespace Leo.Common.Json
{
    public class DateTimeFormatConverter : JsonConverter
    {
        public CultureInfo Culture { get; set; }

        private readonly string DateTimeFormat;
        public DateTimeFormatConverter(string format = "yyyy-MM-dd HH:mm:ss")
        {
            this.DateTimeFormat = format;
            this.Culture = CultureInfo.CurrentCulture;
        }

        public override bool CanConvert(Type objectType)
        {
            return Enumerable.Contains<Type>(new Type[]
            {
                typeof(DateTime),
                typeof(DateTime?),
                typeof(DateTimeOffset),
                typeof(DateTimeOffset?)
            }, objectType);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            bool flag = DateTimeFormatConverter.IsNullable(objectType);
            object value = reader.Value;
            string text = (value != null) ? value.ToString() : null;
            if (string.IsNullOrEmpty(text))
            {
                if (!flag)
                {
                    throw new InvalidDataException("无法将空值转换为" + objectType.Name + "对象，Key=" + reader.Path);
                }
                return null;
            }
            else
            {
                DateTime dateTime = Convert.ToDateTime(text);
                if (!((flag ? Nullable.GetUnderlyingType(objectType) : objectType) == typeof(DateTimeOffset)))
                {
                    return dateTime;
                }
                return new DateTimeOffset(dateTime);
            }
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            if (value == null)
            {
                return;
            }
            string text;
            if (value is DateTime)
            {
                DateTime dateTime = (DateTime)value;
                if (!this.IsDateFormatContainsTimeZone())
                {
                    dateTime = dateTime.ToLocalTime();
                }
                text = dateTime.ToString(this.DateTimeFormat, this.Culture);
            }
            else
            {
                if (!(value is DateTimeOffset))
                {
                    throw new JsonSerializationException(string.Format("Unexpected value when converting date. Expected DateTime or DateTimeOffset, got {0}.", CultureInfo.InvariantCulture, value.GetType().Name));
                }
                DateTimeOffset dateTimeOffset = (DateTimeOffset)value;
                if (!this.IsDateFormatContainsTimeZone())
                {
                    dateTimeOffset = dateTimeOffset.ToLocalTime();
                }
                text = dateTimeOffset.ToString(this.DateTimeFormat, this.Culture);
            }
            writer.WriteValue(text);
        }
         
        private bool IsDateFormatContainsTimeZone()
        {
            return this.DateTimeFormat.Contains("k", StringComparison.OrdinalIgnoreCase) || this.DateTimeFormat.Contains("z", StringComparison.OrdinalIgnoreCase);
        }
         
        private static bool IsNullable(Type objectType)
        {
            return objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(Nullable);
        } 
    }
}
